package com.boot.test.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.*;

import java.io.*;
import java.net.SocketException;

@Slf4j
public abstract class FtpUtil {


    /**
     * @param fileName      ftp 文件名
     * @param ftpUrl        ftp url
     * @param ftpPort
     * @param ftpUsername
     * @param ftpPassword
     * @param ftpRemotePath 远程ftp地址
     * @return
     */
    public static boolean upload(String fileName, String ftpUrl, int ftpPort,
                                 String ftpUsername, String ftpPassword, String localDir, String ftpRemotePath) {
        FTPClient ftpClient = null;
        boolean result = false;
        try {
            ftpClient = getConnection(ftpUrl, ftpPort, ftpUsername, ftpPassword);
            if (ftpClient!=null && ftpClient.isConnected()) {
                result = upload(ftpRemotePath, localDir + "/" + fileName, ftpClient);
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ftpClient != null) {
                logout(ftpClient);
            }
        }
        return result;
    }


    /**
     * @param fileName      ftp 文件名
     * @param ftpUrl        ftp url
     * @param ftpPort
     * @param ftpUsername
     * @param ftpPassword
     * @param ftpRemotePath 远程ftp地址
     * @param ftpDownDir    本地下载地址
     * @return
     */
    public static boolean download(String fileName, String ftpUrl, int ftpPort,
                                   String ftpUsername, String ftpPassword, String ftpRemotePath, String ftpDownDir) {
        boolean result = false;
        FTPClient ftpClient = null;
        try {
            ftpClient = getConnection(ftpUrl, ftpPort, ftpUsername, ftpPassword);
            if (ftpClient!=null &&ftpClient.isConnected()) {
                result = downLoad(ftpRemotePath + "/" + fileName, ftpDownDir, ftpClient);
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ftpClient != null) {
                logout(ftpClient);
            }
        }
        return result;
    }


    /** 将文件从A文件夹移动到B文件夹
     * @param fileName
     * @param ftpUrl
     * @param ftpPort
     * @param ftpUsername
     * @param ftpPassword
     * @param moveAfter
     * @param moveBeafter
     * @return
     */
    public static boolean move(String fileName, String ftpUrl, int ftpPort,
                                   String ftpUsername, String ftpPassword, String moveAfter,String moveBeafter ) {
        boolean result = false;
        FTPClient ftpClient = null;
        try {
            ftpClient = getConnection(ftpUrl, ftpPort, ftpUsername, ftpPassword);
            if (ftpClient!=null &&ftpClient.isConnected()) {
                result = ftpClient.rename(moveAfter+"/"+fileName,moveBeafter+"/"+fileName);
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ftpClient != null) {
                logout(ftpClient);
            }
        }
        return result;
    }



    /**
     * 连接ftp服务器
     *
     * @param host    ip地址
     * @param port    端口号
     * @param account 账号
     * @param pwd     密码
     * @return 是否连接成功
     * @throws SocketException
     * @throws IOException
     */
    private static FTPClient getConnection(String host, int port, String account, String pwd)
            throws SocketException, IOException {
        FTPClient ftpClient = new FTPClient();
        ftpClient.setControlEncoding("UTF-8");
        ftpClient.connect(host, port);

        if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
            ftpClient.login(account, pwd);
            if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                FTPClientConfig config = new FTPClientConfig(ftpClient.getSystemType().split(" ")[0]);
                config.setServerLanguageCode("zh");
                ftpClient.configure(config);
                return ftpClient;
            }
        }
        return null;
    }

    /**
     * 登出并断开连接
     */
    public static void logout(FTPClient ftpClient) {
        if (ftpClient != null && ftpClient.isConnected()) {
            try {
                ftpClient.logout();
                ftpClient.disconnect();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 下载文件到本地地址
     *
     * @param remotePath 远程地址
     * @throws IOException
     */
    public static boolean downLoad(String remotePath, String localDir, FTPClient ftpClient) throws IOException {
        // 进入被动模式
        ftpClient.enterLocalPassiveMode();
        // 以二进制进行传输数据
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        FTPFile[] ftpFiles = ftpClient.listFiles(remotePath);
        if (ftpFiles == null || ftpFiles.length == 0) {
            log.info("远程文件不存在");
            return false;
        } else if (ftpFiles.length > 1) {
            log.info("远程文件是文件夹");
            return false;
        }
        long lRemoteSize = ftpFiles[0].getSize();
        // 本地文件的地址
        File localFileDir = new File(localDir);
        if (!localFileDir.exists()) {
            localFileDir.mkdirs();
        }
        File localFile = new File(localFileDir, ftpFiles[0].getName());
        long localSize = 0;
        FileOutputStream fos = null;
        if (localFile.exists()) {
            if (localFile.length() == lRemoteSize) {
                 return true;
            } else if (localFile.length() < lRemoteSize) {
                // 要下载的文件存在，进行断点续传
                localSize = localFile.length();
                ftpClient.setRestartOffset(localSize);
                fos = new FileOutputStream(localFile, true);
            }
        }
        if (fos == null) {
            fos = new FileOutputStream(localFile);
        }
        InputStream is = ftpClient.retrieveFileStream(remotePath);
        byte[] buffers = new byte[1024];
        long step = lRemoteSize / 10;
        long process = localSize / step;
        int len = -1;
        while ((len = is.read(buffers)) != -1) {
            fos.write(buffers, 0, len);
            localSize += len;
            long newProcess = localSize / step;
            if (newProcess > process) {
                process = newProcess;
            }
        }
        is.close();
        fos.close();
        return ftpClient.completePendingCommand();
    }

    /**
     * 创建远程目录
     *
     * @param remote    远程目录
     * @param ftpClient ftp客户端
     * @return 是否创建成功
     * @throws IOException
     */
    public static boolean createDirectory(String remote, FTPClient ftpClient) throws IOException {
        String dirctory = remote.substring(0, remote.lastIndexOf("/") + 1);
        if (!dirctory.equalsIgnoreCase("/") && !ftpClient.changeWorkingDirectory(dirctory)) {
            int start = 0;
            int end = 0;
            if (dirctory.startsWith("/")) {
                start = 1;
            }
            end = dirctory.indexOf("/", start);
            while (true) {
                String subDirctory = remote.substring(start, end);
                if (!ftpClient.changeWorkingDirectory(subDirctory)) {
                    if (ftpClient.makeDirectory(subDirctory)) {
                        ftpClient.changeWorkingDirectory(subDirctory);
                    } else {
                        return false;
                    }
                }
                start = end + 1;
                end = dirctory.indexOf("/", start);
                if (end <= start) {
                    break;
                }
            }
        }
        return true;
    }

    /**
     * 上传的文件
     *
     * @param remotePath 上传文件的路径地址（文件夹地址）
     * @param localPath  本地文件的地址
     * @throws IOException 异常
     */
    private static boolean upload(String remotePath, String localPath, FTPClient ftpClient) throws IOException {
        // 进入被动模式
        ftpClient.enterLocalPassiveMode();
        // 以二进制进行传输数据
        ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
        File localFile = new File(localPath);
        if (!localFile.exists()) {
            return false;
        }
        String fileName = localFile.getName();
        if (remotePath.contains("/")) {
            boolean isCreateOk = createDirectory(remotePath, ftpClient);
            if (!isCreateOk) {
                return false;
            }
        }

        // 列出ftp服务器上的文件
        FTPFile[] ftpFiles = ftpClient.listFiles(remotePath);
        long remoteSize = 0l;
        String remoteFilePath = remotePath + "/" + fileName;
        if (ftpFiles.length > 0) {
            FTPFile mFtpFile = null;
            for (FTPFile ftpFile : ftpFiles) {
                if (ftpFile.getName().endsWith(fileName)) {
                    mFtpFile = ftpFile;
                    break;
                }
            }
            if (mFtpFile != null) {
                remoteSize = mFtpFile.getSize();
                if (remoteSize == localFile.length()) {
                    return true;
                }
                if (remoteSize > localFile.length()) {
                    if (!ftpClient.deleteFile(remoteFilePath)) {
                    } else {
                        boolean isUpload = uploadFile(remoteFilePath, localFile, 0, ftpClient);
                    }
                    return true;
                }
                if (!uploadFile(remoteFilePath, localFile, remoteSize, ftpClient)) {
                    return true;
                } else {
                    // 断点续传失败删除文件，重新上传
                    if (!ftpClient.deleteFile(remoteFilePath)) {
                    } else {
                        boolean isUpload = uploadFile(remoteFilePath, localFile, 0, ftpClient);
                    }
                    return true;
                }
            }
        }

        return uploadFile(remoteFilePath, localFile, remoteSize, ftpClient);
    }

    /**
     * 上传文件
     *
     * @param remoteFile 包含文件名的地址
     * @param localFile  本地文件
     * @param remoteSize 服务端已经存在的文件大小
     * @return 是否上传成功
     * @throws IOException
     */
    private static boolean uploadFile(String remoteFile, File localFile, long remoteSize, FTPClient ftpClient) throws IOException {
        long step = localFile.length() / 10;
        long process = 0;
        long readByteSize = 0;
        RandomAccessFile randomAccessFile = new RandomAccessFile(localFile, "r");
        OutputStream os = ftpClient.appendFileStream(remoteFile);
        if (remoteSize > 0) {
            // 已经上传一部分的时候就要进行断点续传
            process = remoteSize / step;
            readByteSize = remoteSize;
            randomAccessFile.seek(remoteSize);
            ftpClient.setRestartOffset(remoteSize);
        }
        byte[] buffers = new byte[1024];
        int len = -1;
        while ((len = randomAccessFile.read(buffers)) != -1) {
            os.write(buffers, 0, len);
            readByteSize += len;
            long newProcess = readByteSize / step;
            if (newProcess > process) {
                process = newProcess;
            }
        }
        os.flush();
        randomAccessFile.close();
        os.close();
        return ftpClient.completePendingCommand();
    }

}